package com.zzyl.nursing.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.DeviceDataMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;

/**
 * 规则Service业务层处理
 * 
 * @author shenkunlin
 * @date 2025-08-07
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper,AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IAlertDataService alertDataService;

    //维修工角色
    @Value("${alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;

    //管理员角色
    @Value("${alert.managerRole}")
    private String managerRole;

    /****
     * 报警数据处理
     */
    @Override
    public void alertFilter() {
        //1、查询所有规则(状态=启用)，如果没有规则，直接结束
        Long count = alertRuleMapper.selectCount(Wrappers.<AlertRule>lambdaQuery()
                .eq(AlertRule::getStatus, 1)
        );
        if(count==0){
            return;
        }

        //2、获取所有设备上报数据
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if(CollUtil.isEmpty(values)){
            return;
        }

        //3、数据标准化处理    o=List<DeviceData>
        List<DeviceData> list = new ArrayList<>();
        values.forEach(o->{
            list.addAll(JSONUtil.toList(o.toString(), DeviceData.class));
        });

        //4、遍历所有设备上报数据
        list.forEach(dd->{
            //5、数据必须是1分钟以内的数据，才需要处理
            LocalDateTime alarmTime = dd.getAlarmTime();
            long betweenTimes = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now()).getSeconds();
            if(betweenTimes>60){
                return;
            }

            //6、准备对应报警规则     (某设备的报警规则 + 所有设备报警规则)
            //6.1 当前数据对应的设备报警处理规则
            List<AlertRule> iotRules = alertRuleMapper.selectList(Wrappers.<AlertRule>lambdaQuery()
                    .eq(AlertRule::getIotId, dd.getIotId())
                    .eq(AlertRule::getProductKey, dd.getProductKey())
                    .eq(AlertRule::getProductName, dd.getProductName())
                    .eq(AlertRule::getStatus, 1)
            );

            //6.2 所有设备报警处理规则
            List<AlertRule> allRules = alertRuleMapper.selectList(Wrappers.<AlertRule>lambdaQuery()
                    .eq(AlertRule::getIotId, "-1")
                    .eq(AlertRule::getProductKey, dd.getProductKey())
                    .eq(AlertRule::getProductName, dd.getProductName())
                    .eq(AlertRule::getStatus, 1)
            );

            //6.3 合并规则
            Collection<AlertRule> rules = CollUtil.addAll(iotRules, allRules);
            if(CollUtil.isEmpty(rules)){
                return;
            }

            //7、遍历所有规则，逐条处理
            rules.forEach(r->{
                //8、判断数据上报时间是否在生效时间段内的上报数据
                //8.1 获取有效时间段，按~分割   开始时间=[0]    结束时间=[1]
                String[] times = r.getAlertEffectivePeriod().split("~");
                LocalTime start =LocalTime.parse(times[0]);
                LocalTime end =LocalTime.parse(times[1]);

                //8.2 将数据dd的上报时间alarmTime和开始时间以及结束时间对比，无效直接结束
                LocalTime alTime = dd.getAlarmTime().toLocalTime();
                if(alTime.isBefore(start) || alTime.isAfter(end)){
                    return;
                }

                //9、阈值判断
                //9.1 获取对应数据
                Double dataValue =Double.valueOf(dd.getDataValue()); //上报的数据属性的值
                Double value = r.getValue();   //规则阈值

                //9.2 条件判断   阈值和操作类型（>=  || <）
                String cxKey="cx_"+dd.getIotId()+":"+dd.getFunctionId()+":"+r.getId();   //持续周期的key
                String cmKey="cm_"+dd.getIotId()+":"+dd.getFunctionId()+":"+r.getId();   //沉默周期的key
                int compare = NumberUtil.compare(dataValue, value);
                if((r.getOperator().equals(">=") && compare>=0) || (r.getOperator().equals("<") && compare<0)){
                    //10、沉默周期空判断
                    if(redisTemplate.hasKey(cmKey)){
                        return;
                    }

                    //11、持续周期校验(记录到Redis缓存)
                    //11.1 第1次检验数据，持续周期初始值1，非第1次+1
                    String resultDurtion = redisTemplate.opsForValue().get(cxKey);
                    resultDurtion= ObjUtil.isEmpty(resultDurtion)? "1":String.valueOf(Integer.valueOf(resultDurtion)+1);

                    //11.2 持续周期处理后，如果等于持续周期阈值，则符合报警规则
                    if(ObjUtil.notEqual(resultDurtion,String.valueOf(r.getDuration()))){
                        redisTemplate.opsForValue().set(cxKey,resultDurtion);
                        return;
                    }

                    //11.3 符合报警，删除  持续周期次数 , 添加沉默周期
                    redisTemplate.delete(cxKey);
                    redisTemplate.opsForValue().set(cmKey,"x",Long.valueOf(r.getRemark()), TimeUnit.MINUTES);

                    //12、根据数据类型，查询指定老人的护理员或查询维修人员
                    List<Long> ids = new ArrayList<>();
                    if(r.getAlertDataType()==1){
                        //12.1 设备异常数据-查询维修工
                        ids = sysUserRoleMapper.selectUserIdByRoleName(deviceMaintainerRole);
                    }else{//12.2 老人异常数据
                        //1)随身设备-查询老人的护理员-根据设备ID找到绑定的老人，再根据老人ID找护理员
                        if(dd.getLocationType()==0){
                            ids = deviceMapper.selectNursingIdsByIotIdWithElder(dd.getIotId());
                        }else{
                            //2)固定设备-查询床位绑定的老人的护理员-根据设备ID找到绑定的床位，根据床位绑定的老人匹配老人ID，再查询护理员
                            ids = deviceMapper.selectNursingIdsByIotIdWithBed(dd.getIotId());
                        }
                    }

                    //12.3 查询超级管理员
                    List<Long> managerIds = sysUserRoleMapper.selectUserIdByRoleName(managerRole);
                    ids.addAll(managerIds);
                    if(CollUtil.isEmpty(ids)){
                        return;
                    }

                    //13、批量保存数据到数据库
                    List<AlertData> dataList =
                            ids.stream()
                            .map(id->{
                                //每个ID创建一个AlertData告警数据
                                AlertData data = new AlertData();
                                BeanUtil.copyProperties(dd,data);
                                data.setAlertRuleId(r.getId());
                                String reason=CharSequenceUtil.format("报警原因：{}{}{}，持续周期：{}周期，沉默周期：{}分钟",
                                        dd.getFunctionId(),
                                        r.getOperator(),
                                        r.getValue(),
                                        r.getDuration(),
                                        r.getRemark()
                                        );
                                data.setAlertReason(reason);
                                data.setType(r.getAlertDataType());
                                data.setStatus(0);
                                data.setUserId(id);
                                data.setId(null);
                                return data;
                            }).toList();

                    alertDataService.saveBatch(dataList);

                    //向在线的接收人员发送报警通知
                    AlertData alertData = dataList.get(0);
                    //属性拷贝
                    AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
                    alertNotifyVo.setAccessLocation(alertData.getRemark());
                    alertNotifyVo.setFunctionName(r.getFunctionName());
                    alertNotifyVo.setAlertDataType(r.getAlertDataType());
                    alertNotifyVo.setNotifyType(1);
                    webSocketServer.sendMessageToConsumer(alertNotifyVo,ids);
                }else{
                    // 删除 持续周期的key
                    redisTemplate.delete(cxKey);
                }
            });

        });
    }

    @Autowired
    private WebSocketServer webSocketServer;


    /**
     * 查询规则
     * 
     * @param id 规则主键
     * @return 规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return alertRuleMapper.selectById(id);
    }

    /**
     * 查询规则列表
     * 
     * @param alertRule 规则
     * @return 规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增规则
     * 
     * @param alertRule 规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        alertRule.setCreateTime(DateUtils.getNowDate());
        return alertRuleMapper.insert(alertRule);
    }

    /**
     * 修改规则
     * 
     * @param alertRule 规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        alertRule.setUpdateTime(DateUtils.getNowDate());
        return alertRuleMapper.updateById(alertRule);
    }

    /**
     * 批量删除规则
     * 
     * @param ids 需要删除的规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return alertRuleMapper.deleteByIds(Arrays.asList(ids));
    }

    /**
     * 删除规则信息
     * 
     * @param id 规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return alertRuleMapper.deleteById(id);
    }
}
